package FlowGraph.Transformations;

import java.util.*;

import antlr.collections.AST;

import FlowGraph.*;
import FlowGraph.Nodes.*;
import JavaGrammar.*;
import SymbolTable.*;

//////////////////////////////////////////////////////////////////////////
//// BreakBasicBlock
/**

@author Ivan Jeukens
@version v 0.1 11/11/2002
*/
public class BreakBasicBlock extends Transformation {
    
    public BreakBasicBlock(FlowGraph fg) {
        super(fg);
    }

    ///////////////////////////////////////////////////////////////////
    ////                        public  methods                    ////
    
    /* Para essa transformacao:
       arg0 = basic block
       arg1 = numero do statement onde deve ser feita a divisao
    */
    public void transform(LinkedList arguments) {
        BasicBlockNode node = (BasicBlockNode) arguments.get(0);
        Integer statementIndex = (Integer) arguments.get(1);

        _breakBasicBlock(node, statementIndex.intValue());
    }

    ///////////////////////////////////////////////////////////////////
    ////                        private methods                    ////

    /* Quebra um basic block em um ponto especifico. Essa metodo foi criado
    para permitir a extracao de method calls. */
    private void _breakBasicBlock(BasicBlockNode node, int statementIndex) {
        // Cria o novo no
        BasicBlockNode toadd = new BasicBlockNode();

        _updateState(toadd, node);
        _updateAST(statementIndex, toadd, node);
        _updateConnections(toadd, node);
        _updateSymbolTable(toadd, node, statementIndex);

        _flowGraph.addNode(toadd);
    }

    private void _updateState(BasicBlockNode toadd, BasicBlockNode node) {
        toadd.setUniqueNum(_flowGraph.getUniqueCount());
        _flowGraph.incrementUniqueCount();
        toadd.setScope(node.getScope());

        toadd.setOpenedScopes(node.getOpenedScopes());
        node.setOpenedScopes(0);
    }
    
    private void _updateAST(int statementIndex, BasicBlockNode toadd,
            BasicBlockNode node) {
        // transplanta os statements e statementsInfo
        for(int i = 0;i < statementIndex;i++) {
            toadd.addStatement(node.getStatement(i));
            toadd.setStatementInfo(node.getStatementInfo(i), i);

            // atualiza a conexao entre o no break label e o BB
            for(int j = 0;j < node.inputSize();j++) {
                FlowGraphNode bl = node.getInputNode(j);
                if(bl.getType() == FlowGraphNode.BREAK) {
                    AST target = ((BreakNode)bl).getTarget();
                    if(target != null) {
                        AST v = target.getNextSibling();
                        if(node.getLabel(i).equals(v.getText())) {
                            _flowGraph.replaceConnection(bl, node, toadd);
                        }
                    }
                }
            }
        }
        // remove os statements e labels do no original
        for(int i = 0;i < statementIndex;i++) {
            node.removeStatement(0);
        }
    }
    
    private void _updateConnections(BasicBlockNode toadd, BasicBlockNode node) {
        // atualiza as conexoes
        for(int counter = 0;counter < node.inputSize();counter++) {
            FlowGraphNode n = node.getInputNode(counter);
            // Verifica se nao e uma conexao com um no break com label
            boolean replace = true;
            if(n.getType() == FlowGraphNode.BREAK) {
                AST target = ((BreakNode)n).getTarget();
                if(target != null) {
                    AST v = target.getNextSibling();
                    for(int i = 0;i < node.statementsSize();i++) {
                        if(v.getText().equals(node.getLabel(i))) {
                            replace = false;
                        }
                    }
                }
            }
            if(replace) {
                _flowGraph.replaceConnection(n, node, toadd);
            }
        }
	_flowGraph.connect(toadd, node);
    }

    private void _updateSymbolTable(BasicBlockNode toadd, BasicBlockNode node,
            int statementIndex) {
        // Atualiza as STs. Verifica se existia alguma definicao no no antigo.
        // Se nao, e so ligar o st do no novo
        if(node.hasVarDef()) {
            // atualiza as symboltables. Verifica cada statement transferido
            // para a definicao de uma variavel. Caso positivo, transfere a
            // entrada da ST
            SymbolTable st = node.getSymbolTable();
            for(int i = 0;i < statementIndex;i++) {
                AST stat = toadd.getStatement(i);
                if(stat.getType() == JavaTokenTypes.VARIABLE_DEF) {
                     AST v = stat.getFirstChild();
                     v = v.getNextSibling();
                     v = v.getNextSibling();
                     STEntry entry = (STEntry) st.removeElement(v.getText());

                     SymbolTable toaddST = toadd.getSymbolTable();
                     if(toaddST == null) {
                         toaddST = new SymbolTable();
                         toadd.setSymbolTable(toaddST, true);
                     }
                     toaddST.putElement(entry.getName(), entry);
                }
            }
        
            // Verifica se sobrou alguma definicao de variavel no no antigo
            node.setVarDef(false);
            for(int i = 0;i < node.statementsSize();i++) {
                AST stat = node.getStatement(i);
                if(stat.getType() == JavaTokenTypes.VARIABLE_DEF) {
                    node.setVarDef(true);
                    break;
                }
            }

            // conecta as STs. Verifica se alguma definicao foi transferida
            // ou nao.
            SymbolTable toaddST = toadd.getSymbolTable();
            SymbolTable parent = st.getNextTable();
            if(toaddST == null) {
                toadd.setSymbolTable(parent, false);
            }
            else {
                // Verifica se ainda existe alguma definicao no no antigo
                if(st.size() == 0) {
                    toaddST.link(parent);
                    node.setSymbolTable(toaddST, true);
                }
                else {
                    toaddST.link(parent);
                    st.link(toaddST);
                }
            }
        }
        else {
            SymbolTable st = node.getSymbolTable();
            toadd.setSymbolTable(st, false);
        }
    }

    private void _updateDominatorTree() {

    }

    ///////////////////////////////////////////////////////////////////
    ////                      private variables                    ////

}
